#if defined(_MSC_VER)
#pragma once
#endif

#ifndef LM_CORE_LM_H
#define LM_CORE_LM_H


// C_plus_plus header
#include <cstdio>
#include <cstdlib>
#include <iostream>
#include <fstream>
#include <sstream>
#include <vector>
#include <set>
#include <cmath>
#include <string.h>
#include <string>
#include <assert.h>
#include <algorithm>
#include <malloc.h>
#include <float.h>
#include <time.h>

// Console print function
#include "console.h"
#include "mathutil.h"

#define isnan _isnan
/*
#define isnan(x) ((x) != (x))
*/
#define isinf(f) (!_finite((f)))

#define uint unsigned int 
#define int8_t __int8
#define uint8_t unsigned __int8
#define int16_t __int16
#define uint16_t unsigned __int16
#define int32_t __int32
#define uint32_t unsigned __int32
#define int64_t __int64
#define uint64_t unsigned __int64

#pragma warning (disable : 4305) // double constant assigned to float
#pragma warning (disable : 4244) // int -> float conversion
#pragma warning (disable : 4267) // size_t -> unsigned int conversion
#pragma warning (disable : 4996) // fopen -> fopen_s
#pragma warning (disable : 4018) // signed/unsigned mismatch in for loop

using std::vector;
using std::set;
using std::string;
using std::max;
using std::min;
using std::sort;
using std::swap;

// Global Macros
#define ALLOCA(TYPE, COUNT) (TYPE *)alloca((COUNT) * sizeof(TYPE))
#define EPSILON(i) (1e-i)
#define RAYEPSILON 1e-6
#define alloca _alloca

// Compare method for float
#define FLOATEQ(a,b) fabs((a) - (b)) < 1e-6 ? 1 : 0
#define FLOATNEQ(a, b) (!(FLOATEQ(a, b)))


#define MIN3(x, y, z) (                     \
	std::min(std::min((x), (y)), (z))        \
	)

#define MAX3(x, y, z) (                     \
	std::max(std::max((x), (y)), (z))        \
	)

// Use custom random number generator
// Undef this to use rand() and srand() provided by standard library
#define USE_CUSTOM_RNG

// Convert from float to rgb with gamma correction
#define TO_BYTE(v) (uint8_t(::Clamp(255.f * powf((v), 1.f/2.3f), 0.f, 255.f)))

// Safe Delete Macro, from DXUT.h
#ifndef SAFE_DELETE
#define SAFE_DELETE(p)       { if (p) { delete (p);     (p)=NULL; } }
#endif
#ifndef SAFE_DELETE_ARRAY
#define SAFE_DELETE_ARRAY(p) { if (p) { delete[] (p);   (p)=NULL; } }
#endif

/*
	#define SAFE_DELETE(p)   \
	do {                 \
	if ((p)) {        \
	delete ((p));  \
	(p) = NULL;    \
	}                 \
	} while(0)

	#define SAFE_DELETE_ARRAY(p) \
	do {                    \
	if ((p)) {           \
	delete[] ((p));   \
	(p) = NULL;       \
	}                    \
	} while(0)
*/


#ifdef NDEBUG
#define Assert(expr) ((void)0)
#else
#define Assert(expr) \
	((expr) ? (void)0 : \
	Fatal("Assertion \"%s\" failed in %s, line %d", \
#expr, __FILE__, __LINE__))
#endif

// Foreward Declaration, all class & struct
class Vector2;
class Vector3;
class Vector4;
class Point2;
class Point3;
class Normal;
class Matrix4x4;
class Frame;
class Ray;
class RayDifferential;
class Transform;
class Camera;
class Film;
class RNG;
class ReferenceCounted;
template <typename T> class Reference;
template <typename T>class ObjectArena;
class MemoryPool;
template <typename T, int logBlockSize = 2> class BlockedArray;
class BBox;
class Spectrum;
class Filter;
class Shape;
template<typename T> class Texture;
class TextureMap;
class BxDF;
class BSDF;
class Material;
class Shape;
class Light;
class AreaLight;

class SurfaceIntegrator;
class VolumeIntegrator;
class Scene;

class Sampler;


// Global Constants
#define LM_VERSION "0.0.1"
#ifdef M_PI
#undef M_PI
#endif
#define M_PI		3.14159265358979323846
#define INV_PI		0.31830988618379067154
#define INV_TWOPI	0.15915494309189533577
#define INV_FOURPI	0.07957747154594766788
#define INV_255		0.00392156862745098039

static const float OneMinusEpsilon=0.9999999403953552f;

#ifndef INFINITY
#define INFINITY FLT_MAX
#endif

// Global Functions
inline float Lerp(float t, float v1, float v2) {
	return (1.f - t) * v1 + t * v2;
}

inline float Clamp(float val, float low, float high) {
	if (val < low) return low;
	else if (val > high) return high;
	else return val;
}

inline int Clamp(int val, int low, int high) {
	if (val < low) return low;
	else if (val > high) return high;
	else return val;
}

// Some Math Stuffs
inline int Mod(int a, int b) {
	int n = int(a/b);
	a -= n*b;
	if (a < 0) a += b;
	return a;
}

inline float Radians(float deg) {
	return ((float)M_PI/180.f) * deg;
}

inline float Degrees(float rad) {
	return (180.f/(float)M_PI) * rad;
}

inline float Log2(float x) {
	static float invLog2 = 1.f / logf(2.f);
	return logf(x) * invLog2;
}

inline int Floor2Int(float val);
inline int Log2Int(float v) {
	return Floor2Int(Log2(v));
}

inline bool IsPowerOf2(int v) {
	return ((v & (v - 1)) == 0) && v;	// v != 0
}

inline uint32_t RoundUpPow2(uint32_t v) {
	v--;
	v |= v >> 1;    v |= v >> 2;
	v |= v >> 4;    v |= v >> 8;
	v |= v >> 16;
	return v+1;
}

inline int Floor2Int(float val) {
	return (int)floorf(val);
}

inline int Round2Int(float val) {
	return Floor2Int(val + 0.5f);
}

inline int Float2Int(float val) {
	return (int)val;
}

inline int Ceil2Int(float val) {
	return (int)ceilf(val);
}

// Global variables


#endif	// LM_CORE_LM_H
